/*!
 * Local Environment Chat System ("LEChat") JS file
 * (c) 2011 by Sascha René Leib, released under the GPL
 * Version: 0.6 of 24.06.2012
 * -- requires jQuery 1.6 or later --
 */

lcMessagerie = {

	// settings:
	requestUrl : "ajax/lechat.php", // where to send all requests?
	mailcheckFrequency:	2500, // ms
	messageLimit: 140, // characters

	// initialization function:
	initialize: function() {

		// initialize the GUI:
		lcMessagerie.gui.initialize();
		
		// initialize the users list:
		lcMessagerie.users.initialize();
		
		// start the message checking updater::
		lcMessagerie.updater.initialize();
		
	}
}

// GUI-related items:
lcMessagerie.gui = {

	// the system account's display name:
	systemAccountName : "system",

	// if the last message was an error, this will store the error code:
	lastErrorCode : 0,

	// initialize GUI only:
	initialize : function() {

		// delegate callback for click on message text:
		$("#msgroot").delegate("li div.msgwrapper p.msgbody", "click", function() {
			lcMessagerie.gui.callback.messageListItemClick($(this));
		});
		
		// callback delegate for send buttons:
		$("body").delegate("button[type=submit]", "click", function() {
			lcMessagerie.gui.callback.messageSendClick($(this));
		});
		$("body").delegate("button[type=reset]", "click", function() {
			lcMessagerie.gui.callback.messageCancelClick($(this));
		});
		$("body").delegate("#ultab", "click", function() {
			$("#ulcontent").toggle();
		});

		// new message GUI items callbacks:
		$("body").delegate("textarea", "keydown", function(evt) {
			return lcMessagerie.gui.callback.messageEditorKeyDown($(this), evt);
		});		
		
		$("#newmsg").delegate("button[type=submit]", "click", function() {
			lcMessagerie.gui.callback.messageSendClick($(this));
		});

		// header items:
		$("header").delegate("#headerAboutLink", "click", function() {
			lcMessagerie.gui.callback.headerAboutLinkClick($(this));
		});
		
		// about box close button:
		$("aside#about").delegate("#aboutCloseButton", "click", function() {
			lcMessagerie.gui.callback.aboutCloseButtonClick($(this));
		});
		
		// show/hide GUI items:
		$('aside#newmsg').show();
		$('aside#loading').hide();

	},

	// show the reply-to-message elements
	msgReplyItemsShow : function(wrapper) {

		var answer = $('<div class="msgAnswer"></div>');
		answer.append($('<div class="msgcharcount">#</div>'));
		answer.append($('<h2>Reply to this message:</h2>'));
		answer.append($('<textarea name="msg" cols="60" rows="1"></textarea>'));
		answer.append($('<button type="reset" value="0">Cancel</button>'));
		answer.append($('<button type="submit" value="1">Answer</button>'));
	
		wrapper.append(answer);
		wrapper.addClass('bordered');
	},

	// hide the reply-to-message elements
	msgReplyItemsHide : function(wrapper) {
		wrapper.removeClass('bordered');
		var tools = wrapper.children('div.msgAnswer');
		if (tools.length > 0) {
			tools.remove();
		} else {
			$('#msg').val('');
		}
	},

	// inserts a single message to the appropriate place in the DOM:
	insertMessageToPage : function(msgId, userName, msgText, msgTimestamp, replyToId, cssExtra) {

		var date = new Date(msgTimestamp*1000);
		var timeString = lcMessagerie.utilities.formatDate(date);
		var dateString = date.toLocaleString();
		
		var cssClass = "msgbody" + (cssExtra != '' ? ' ' + cssExtra : '');

		// create the message element:
		var paragraph = $('<p class="' + cssClass + '"></p>').append(
							// $('<em>id ' + msgId + ' - in reply to ' + replyToId + ';&nbsp;</em>'),
							$('<span class="msgdate" title="'+dateString+'">' + timeString + '</span>'),
							$('<span class="username">' + userName + '</span>'),
							$('<span class="msgtext">' + msgText + '</span>')
						);
		
		var msgdiv = $('<div class="msgwrapper"></div>').append(paragraph);
		msgdiv.data('msg_id', msgId);

		var listItem = $('<li class="msg" id="msg' + msgId + '"></li>').append(msgdiv);

		if (replyToId<=0) {
			$('#msgroot').prepend(listItem);
		} else {
			var msgobj = $('#msg' + replyToId);
			
			if (msgobj.length>0) {
			
				var uls = msgobj.children('ul');
				if (uls.length > 0) {
					var ul = $(uls[0]);
				} else {
					var ul = $('<ul />');
					msgobj.append(ul);
				}
				ul.prepend(listItem);

			} else {
				// first create a dummy parent:
				var parentDiv = $('<div class="msgwrapper"><p class="msgbody"><span class="msgtext"><i>&hellip;</i></span></p></div>');
				var parentItem = $('<li class="msg dummy" id="msg' + replyToId + '"></li>').append(parentDiv);
				var ul = $('<ul />').append(listItem);
				parentItem.append(ul);
				$('#msgroot').append(parentItem);
				
			}
		}
		
		// clear any possible error code:
		lcMessagerie.gui.lastErrorCode = 0;
	},

	// insert an error message to the message stream:
	insertErrorMsgObjToPage  : function(msgObj) {
	
		// read the error object:
		var error = msgObj['error'];
		if (error !== undefined) {
	
			var code = ((error['code'] !== undefined) ? parseInt(error['code']) : 0 );
			if (code != lcMessagerie.gui.lastErrorCode) {
				var msg = ((error['msg'] !== undefined) ? error['msg'] : 'No message.' );
				var now = new Date(); var ts = (now.getTime() / 1000);

				// insert the error message to the GUI:
				lcMessagerie.gui.insertMessageToPage(
					-1,
					lcMessagerie.gui.systemAccountName,
					msg,
					ts,
					0,
					'system'
				);
				
				// store the last error code
				lcMessagerie.gui.lastErrorCode = code;
			} // if error.code != lastErrorCode
		} // if error !== undefined
	},
	
	// insert a message object to the page
	insertMsgObjToPage : function(msgObj) {

		var msg = msgObj['message'];
		var ts = ((msg['ts'] !== undefined) ? parseInt(msg['ts']) : 0 );
		lcMessagerie.updater.updateTimestamp(ts);
		
		// insert the message to the GUI:
		lcMessagerie.gui.insertMessageToPage(
			msg.id,
			msg.user,
			msg.message,
			msg.ts,
			msg.replyto
		);
	},
	
	// insert or update a user information:
	insertUpdateUser : function(id, name, status) {
	
		var parent = $('#otheruserslist');
		
		// check if it already exists:
		var item;
		var items = parent.find('usr'+id);
		if (items.length<=0) {
			item = $('<li />');
			parent.append(item);
		} else {
			item = items[0];
		}
		
		// update the item
		item.text(name);
		item.attr('class', status);
		item.attr('id', 'usr'+id);
	}

}

// GUI callbacks:
lcMessagerie.gui.callback = {

	// when a message has been clicked:
	messageListItemClick : function(item) {
		// only if it isn't open yet:
		var wrapper = item.parent();
		if ((!wrapper.hasClass('bordered')) && (wrapper.data('msg_id')>0) && (!wrapper.parent().hasClass('dummy'))) {
			lcMessagerie.gui.msgReplyItemsShow(wrapper);
		}
	},
	
	// when a send button has been clicked:
	messageSendClick : function(item) {
	
		var answerDiv = item.parent();
		var wrapper = answerDiv.parent();
		var replyTo = wrapper.data('msg_id');
		var textarea = wrapper.find('textarea');
		var msgText = $(textarea[0]).val();

		// enqueue the message to be sent:
		lcMessagerie.updater.enqueueMessage(msgText, (replyTo !== undefined ? replyTo : 0));
		
		// reset the message:
		lcMessagerie.gui.msgReplyItemsHide(wrapper);

		return true; // override the button function
	},

	// when a cancel button has been clicked:
	messageCancelClick : function(item) {
		var wrapper = item.parent().parent();
		lcMessagerie.gui.msgReplyItemsHide(wrapper);
	},
	
	// when the About link is clicked:
	headerAboutLinkClick : function(item) {
		$("aside#about").show();
		$("#aboutCloseButton").focus();
	},

	// when the About box' close button is clicked:
	aboutCloseButtonClick : function(item) {
		$("aside#about").hide();
	},

	// when a key is pressed in the text editor:
	messageEditorKeyDown : function(context, evt) {
	
		var keyCode = evt.keyCode;
		var wrapper = context.parent().parent();
		var wrapperId = wrapper.attr('id');
		
		// special treatment for all control codes, except 8 (=backspace):
		if (keyCode === 8)	// backspace
		{
			return true; // allow
		}
		 else if (keyCode === 13)	// return = send message
		{
			var sendButton = wrapper.find("button[type=submit]")[0];
			try
			{
				lcMessagerie.gui.callback.messageSendClick($(sendButton));
			}
			 finally
			{
				return false;
			}
		}
		 else if (keyCode === 27)	// escape = cancel message
		{
			if (wrapperId !== 'newmsg') {
				lcMessagerie.gui.msgReplyItemsHide(wrapper);
			}
		}
		 else if (keyCode < 32) // other low-ascii chars
		{
			return false; // ignore
		}
		 else // any other char
		{
			var newCharCount = $(context).val().length + (keyCode == 8 ? -1 : 1);
			var remainingChars = lcMessagerie.messageLimit - newCharCount;
			var countField = wrapper.find(".msgcharcount")[0];
			
			$(countField).text(remainingChars);
			
			return true;
		}
	}
}

// manage regular updates 
lcMessagerie.updater = {

	// updater private properties:
	latestTimestamp : 0,
	updateTimestamp : function(newVal) {
		if (newVal > this.latestTimestamp) {
			this.latestTimestamp = newVal;
		}
	},

	// updater send message:
	msgToSend : "",
	msgInReplyTo : 0,
	
	// update user name:
	userNameToSend : "",

	// start the update timer
	initialize : function() {
		try {
			// load  messages:
			lcMessagerie.updater.sendServerRequest();
		} catch (err) {
			// show err as system message
		} finally {
			// restart tick timer
			setInterval("lcMessagerie.updater.sendServerRequest()", lcMessagerie.mailcheckFrequency);
		}
	},
	
	// add a new message to send:
	enqueueMessage : function(text, inReplyTo) {
		lcMessagerie.updater.msgToSend = text;
		lcMessagerie.updater.msgInReplyTo = inReplyTo;
	},

	// send the request to the server:
	sendServerRequest : function() {

		var sendData = {'after' : lcMessagerie.updater.latestTimestamp };
		
		// set the message to send:
		if (lcMessagerie.updater.msgToSend != "") {
			sendData.msg = lcMessagerie.updater.msgToSend;
			sendData.replyTo = lcMessagerie.updater.msgInReplyTo;
		};
		
		// ask to change the user name, if requested
		if (lcMessagerie.updater.userNameToSend != "") {
			sendData['setname'] = lcMessagerie.updater.userNameToSend;
			lcMessagerie.updater.userNameToSend = '';
		};

		$.ajax({
			url:		lcMessagerie.requestUrl,
			type:		"post",
			data:		sendData,
			context:	document.body,
			success:	function(data)
						{
							lcMessagerie.updater.msgToSend = "";
							jQuery.each(data, function(i, msg) {
								lcMessagerie.updater.dispatchMsg(msg);
							});
						},
			error:		function(jqXHR, textStatus, errorThrown)
						{
							var code = -1024;
							var now = new Date();
							if (code != lcMessagerie.gui.lastErrorCode) {
								lcMessagerie.gui.insertMessageToPage(
									-2,
									lcMessagerie.gui.systemAccountName,
									"Connection error.",
									(now.getTime() / 1000),
									0,
									'system'
								)
								// store the last error code
								lcMessagerie.gui.lastErrorCode = code;
							} // if error.code != lastErrorCode
						},
			async:		true
		});
	},
	
	// dispatch a server message object:
	dispatchMsg : function(msgObj) {
	
		var cmd = msgObj['cmd'];
		
		if (cmd !== undefined) {
			if ((cmd == 'msg:new') && (msgObj['message'])) {

				lcMessagerie.gui.insertMsgObjToPage(msgObj);

			} else if ((cmd == 'usr:upd') && (msgObj['user'])) {

				lcMessagerie.users.insertUpdateUser(msgObj);

			} else if ((cmd == 'err:msg') && (msgObj['error'])) {

				lcMessagerie.gui.insertErrorMsgObjToPage(msgObj);

			} else if (cmd == 'init') {
			
				lcMessagerie.users.setUserInit(msgObj);
			
			}
		}
	}
}

// various utility functions:
lcMessagerie.users = {

	// my user id on the server:
	myUserId : 0,
	myUserName : "",
	
	// Users initialisation:
	initialize : function() {
		
		$("#ulMyName").focusin(function() {
			lcMessagerie.users.gui.callback.userNameFieldActivate();
		});
		$("#ulMyNameChange").click(function() {
			lcMessagerie.users.gui.callback.userNameChangeClick();
		});
		$("#ulMyNameCancel").click(function() {
			lcMessagerie.users.gui.callback.userNameCancelClick();
		});
		$("#ulMyName").keydown(function(evt) {
			return lcMessagerie.users.gui.callback.userNameKeyDown(evt);
		});

		lcMessagerie.users.gui.prepareUsersList();
	},
	
	// set user information by server message
	setUserInit : function(msgObj) {
	
		var usr = msgObj['user'];

		// store the user name and id:
		lcMessagerie.users.myUserName = ((usr['name'] !== undefined) ? usr['name'] : 'unset' );
		lcMessagerie.users.myUserId = ((usr['id'] !== undefined) ? parseInt(usr['id']) : 0 );
		
		// set the display of the user name:
		lcMessagerie.users.gui.changeMyUsername(usr['name']);
	},

	// insert/update user information:
	insertUpdateUser : function(msgObj) {

		var usr = msgObj['user'];
		
		var ts = ((usr['ts'] !== undefined) ? parseInt(usr['ts']) : 0 );
		lcMessagerie.updater.updateTimestamp(ts);
		
		lcMessagerie.users.gui.insertUpdateUserInfo(
			usr.id,
			usr.name,
			usr.st
		);
	}
}

lcMessagerie.users.gui = {

	prepareUsersList : function() {
		
		var availableWidth = $('body').innerWidth();
		availableWidth -= $('#messagelist').outerWidth();
		availableWidth -= $('#ultab').outerWidth();
		availableWidth -= $('#ulcontent').outerWidth();
		
		if (availableWidth < 32) {
			$("#ulcontent").hide();
		}
		
	},
	
	// insert or update a user information:
	insertUpdateUserInfo: function(id, name, status) {
	
		if (id == lcMessagerie.users.myUserId) {
			lcMessagerie.users.gui.changeMyUsername(name);
			// hide background-operations indicator:
			$('#myNameBusyIndicator').hide();
		} else {
		
			// any other user:
			var parent = $('#otheruserslist');
			
			// check if it already exists:
			var item;
			var items = $('#usr'+id);
			if (items.length>0) {
				item = $(items[0]);
				if (status == 'void') {
					item.detach();
				} else {
					item.attr('class', status);
					item.attr('id', 'usr'+id);
					item.text(name);
				}
			} else if (status != 'void') {
				item = $('<li id="usr'+id+'" class="'+status+'">'+name+'</li>');
				parent.append(item);
			}
		}
	},

	// change my user name in GUI:
	changeMyUsername : function(newName) {

		var nameField = $('#ulMyName');
		nameField.val(newName);
	}
}

lcMessagerie.users.gui.callback = {

	// callback for activating the user name field
	userNameFieldActivate : function() {
	
		$('#ulMyNameTools').show();
	
	},
	
	// callback for clicking on "cancel" under the user name field
	userNameCancelClick : function() {
	
		$('#ulMyName').val(lcMessagerie.users.myUserName);
		$('#ulMyNameTools').hide();
	
	},
	
	// callback for clicking on "save" under the user name field
	userNameChangeClick : function() {
	
		var newUserName = $('#ulMyName').val();
		if (newUserName != lcMessagerie.users.myUserName) {
			// show the old user name until the update is finished:
			$('#ulMyName').val(lcMessagerie.users.myUserName);
			// indicate that something is running in the background:
			$('#myNameBusyIndicator').show();
			// prepare to send the new user name:
			lcMessagerie.updater.userNameToSend = newUserName;
			
			// save this as the new "preferred" username to a cookie:
			
			var cookieDate = new Date( );
			cookieDate.setTime(cookieDate.getTime() + (30*86400000)); /*  = 24*60*60*1000 */
			document.cookie = "preferred_username=" + escape(newUserName) +
				"; expires=" + cookieDate.toGMTString( ) + "; path=/";
		}

		$('#ulMyNameTools').hide();
	},

	// callback for keydown events in the username field:
	userNameKeyDown : function(evt) {
		var keyCode = evt.keyCode;

		if (keyCode === 8) { // backspace
			return true;
		}
		 else if (keyCode === 13) // return
		{	// OK to change name
			lcMessagerie.users.gui.callback.userNameChangeClick();
			$('#msg').focus();
		}
		 else if (keyCode === 27) // escape
		{	// cancel name change
			lcMessagerie.users.gui.callback.userNameCancelClick();
			$('#msg').focus();
		}
		 else if (keyCode <= 32) // other low-ascii codes (incl. whitespace!)
		{
			return false;
		}
		 else
		{
			length = $('#ulMyName').val().length;
			return (length <= 16);
		}

	}
}

// various utility functions:
lcMessagerie.utilities = {

	formatDate : function(date) {

		try {

			var yesterday = new Date();
            yesterday.setDate(yesterday.getDate() - 1);

			if (date < yesterday) {
				return 'older';
			} else {
				return ('0'+date.getHours()).slice(-2) +':' + ('0'+date.getMinutes()).slice(-2);
			}
		
		} catch (err) {
			return 'error';
		}
	}

}

// initialize on startup:
$(document).ready(function() {

	lcMessagerie.initialize();

});
